#!/bin/bash
set -x
source /etc/os-release
THIS_SCRIPT_PATH=$(dirname $(readlink -f $0))
REGISTRY=${REGISTRY:-registry.redhat.io}
PLATFORM=${PLATFORM:-${ID#rh*}${VERSION_ID%.*}}
IS_RT_CAPABLE="false"
if uname -v | grep -q PREEMPT; then
    IS_RT_CAPABLE="true"
fi
DEFAULT_HOST_INTERFACE_NAME=$(ip route get 1.1.1.1 | grep -oP 'dev \K\S+')
DEFAULT_HOST_INTERFACE_IP=$(ip route get 1.1.1.1 | grep -oP 'src \K\S+')

info() {
    local MESSAGE=$1

    echo -e "\E[34m\n== $MESSAGE\E[00m";
}

usage() {
    echo "Runs the specified (set of) OAI components locally on Podman for testing purposes.
    
usage: $(basename $0) [-h] COMPONENT1 [COMPONENT2 [...]]
  -h|--help:        prints this help message
  
available components:
  HSS:      Runs oai-hss + Cassandra.
  MME:      Runs oai-mme.
  (SPGW:     Runs oai-spgw-c + oai-spgw-u.)
  EPC:      Alias for \"HSS MME SPGW\".
  RCC:      Runs oai-enb in RCC mode, accessing external RRU via IF4.5.
  eNB+UE:   Runs oai-enb in RCC mode, with oai-ue for RRU, channel sim, and UE emulation."
    exit 1
}

wait_for_state() {
    local CONTAINER_NAME_OR_ID=$1   # container name or id to monitor
    local TARGET_STATE=${2,,}       # state to waite for ("running", "exited", etc)
    local TIMEOUT=${3:-30}          # timeout (default: 30s)

    until [ "$(podman inspect ${CONTAINER_NAME_OR_ID} -f {{.State.Status}})" == "${TARGET_STATE}" ] || [ $TIMEOUT -le 0 ]; do
        echo -ne "  Waiting ${TIMEOUT}s for ${CONTAINER_NAME_OR_ID} to transition to state ${TARGET_STATE^^}.\033[0K\r"
        sleep 1;
        (( TIMEOUT-- ))
    done
    if [ $TIMEOUT -le 0 ]; then
        info "${CONTAINER_NAME_OR_ID} failed to transition to ${TARGET_STATE^^}."
        exit 1
    fi
}

wait_for_socket() {
    local HOST=$1           # host to use for connectivity test
    local PORT=$2           # port to use for connectivity test
    local TIMEOUT=${3:-120} # timeout (default: 120)

    until nc -z $HOST $PORT || [ $TIMEOUT -le 0 ]; do
        echo -ne "  Waiting ${TIMEOUT}s for port $PORT on host $HOST to open.\033[0K\r"
        sleep 1;
        (( TIMEOUT-- ))
    done
    if [ $TIMEOUT -le 0 ]; then
        info "Port $PORT on host $HOST failed to open."
        exit 1
    fi
}

run_component() {
    local IMAGE=$1
    local CONTAINER_NAME=$2
    local LIVENESS_IP=${3%:*}
    local LIVENESS_PORT=${3#*:}

    if [ "$(podman ps -a -q -f name=$CONTAINER_NAME)" ]; then
        if [ "$(podman inspect -f {{.State.Running}} ${CONTAINER_NAME})" == "true" ]; then
             info "$CONTAINER_NAME container already running."
             return
        fi
        info "Removing existing $CONTAINER_NAME container."
        podman rm -f $CONTAINER_NAME 2> /dev/null
    fi

    info "Starting $CONTAINER_NAME container."
    shift 3 # remove image, container name and liveness IP+port from args
    echo -ne "podman run -d --net=host --name $CONTAINER_NAME $@ $IMAGE\n"
    podman run -d --net=host --name $CONTAINER_NAME "$@" $IMAGE

    # block until container is running and service binds to port
    wait_for_state $CONTAINER_NAME "RUNNING"
    wait_for_socket $LIVENESS_IP $LIVENESS_PORT
}

enable_sctp() {
    if ! $(lsmod | grep -q sctp); then
        info Enabling SCTP
        dnf install -y kernel-modules-extra
        rm /etc/modprobe.d/sctp-blacklist.conf
        modprobe sctp
    fi
}

# parse command line args
POSITIONAL=()
while [[ $# -gt 0 ]]; do
    key="$1"
    case $key in
        -h|--help)
        usage
        ;;
        *) # unknown option
        POSITIONAL+=("$key") # save it in an array for later
        shift # past argument
        ;;
    esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters

# if there are positional args, interpret them as list of components to run
if [ $# -eq 0 ]; then
    echo "Please provide at least one component to run."
    usage
fi
COMPONENTS=$*
COMPONENTS=${COMPONENTS^^}
COMPONENTS=${COMPONENTS/EPC/HSS MME SPGW}


if [[ "${COMPONENTS}" =~ "HSS" ]]; then
    run_component cassandra:3.11 cassandra 127.0.0.1:9042 \
        -e CASSANDRA_DC=DC1 \
        -e CASSANDRA_RACK=RACK1 \
        -e CASSANDRA_CLUSTER_NAME=OAI_HSS \
        -e CASSANDRA_ENDPOINT_SNITCH="GossipingPropertyFileSnitch" \
        -p 127.0.0.1:9042:9042 
    CASSANDRA_IP=$(podman exec cassandra ip route get 1.1.1.1 | grep -oP 'src \K\S+')

    info 'Provisioning HSS database schema'
    QUERIES=$(<${THIS_SCRIPT_PATH}/db/oai_db.cql)
    podman run --rm -it cassandra:3.11 cqlsh ${CASSANDRA_IP} -e "${QUERIES}"

    run_component oai-hss_rel14:k8s oai-hss 127.0.0.1:3868 \
        -e DB_FQDN=${CASSANDRA_IP} \
        -e DB_NAME=vhss \
        -e DB_USER=cassandra \
        -e DB_PASSWORD=cassandra \
        -e OP_KEY="11111111111111111111111111111111" \
        -e HSS_FQDN="hss.openair4G.eur" \
        -e ROAMING_ALLOWED="true" \
        -p 3868:3868 -p 5868:5868 -p 9080:9080 -p 9081:9081 \
        -v ${PWD}/manifests/oai-hss/certs:/opt/oai-hss/certs:Z \
        -h hss.openair4G.eur

    info 'Registering subscriber(s) with HSS'
    QUERIES=$(<${THIS_SCRIPT_PATH}/db/subscribers.cql)
    podman run --rm -it cassandra:3.11 cqlsh ${CASSANDRA_IP} -e "${QUERIES}"
fi

if [[ "${COMPONENTS}" =~ "SPGW" ]]; then
    echo "(not implemented yet)"
    # Commented due to  podman run --net=host (Same port binding for S11, SX)
    #run_component oai-spgwc:develop oai-spgwc 127.0.0.1:2123 \
    #    -e SGW_S11_INTERFACE="$CONTAINER_INTERFACE" \
    #    -e PGW_SX_INTERFACE="$CONTAINER_INTERFACE" \
    #    -e UE_IP_ADDRESS_POOL="10.0.23.1 - 10.0.23.240" \
    #    -e UE_DNS_SERVER="8.8.8.8" \
    #    -p 2123:2123 -p 8805:8805 \
    #    -h spgwc.openair4G.eur
    #PGWC_SX_IP_ADDRESS=`sudo podman exec -it oai-spgwc ip -4 addr show  scope global | awk '$1 == "inet" {print $2}' | xargs | cut -d '/' -f1`
    #SGWC_IP=`sudo podman exec -it oai-spgwc ip -4 addr show  scope global | awk '$1 == "inet" {print $2}' | xargs | cut -d '/' -f1`

    # Hardcoded due to  podman run --net=host 
    # PGWC_SX_IP_ADDRESS="192.168.104.200"
    # SGWC_IP="192.168.104.199"

    # run_component oai-spgwu:develop oai-spgwu 127.0.0.1:2123 \
    #     -e SGW_S1U_INTERFACE="$CONTAINER_INTERFACE" \
    #     -e SGW_SX_INTERFACE="$CONTAINER_INTERFACE" \
    #     -e PGW_SGI_INTERFACE="$CONTAINER_INTERFACE" \
    #     -e NETWORK_UE_IP="10.0.23.0/24" \
    #     -e PGWC_SX_IP_ADDRESS="$PGWC_SX_IP_ADDRESS" \
    #     -p 2123:2123 -p 8805:8805 \
    #     -h spgwu.openair4G.eur
fi

if [[ "${COMPONENTS}" =~ "MME" ]]; then
    enable_sctp

    info 'Registering MME with HSS'
    QUERIES="INSERT INTO vhss.mmeidentity (idmmeidentity, mmeisdn, mmehost, mmerealm, ue_reachability) VALUES (4, 'mme-isdn', 'mme.openair4G.eur', 'openair4G.eur', 1); INSERT INTO vhss.mmeidentity_host (idmmeidentity, mmeisdn, mmehost, mmerealm, ue_reachability) VALUES (4, 'mme-isdn', 'mme.openair4G.eur', 'openair4G.eur', 1);"
    podman run --rm -it cassandra:3.11 cqlsh ${CASSANDRA_IP} -e "${QUERIES}"

    HSS_IP=$(podman exec oai-hss ip route get 1.1.1.1 | grep -oP 'src \K\S+')
    run_component oai-mme:master oai-mme 127.0.0.1:3870 \
        --privileged \
        -e MME_SERVICE="mme.openair4G.eur" \
        -e MME_REALM="openair4G.eur" \
        -e MME_GID="4" \
        -e MME_CODE="1" \
        -e MCC="208" \
        -e MNC="92" \
        -e TAC="1" \
        -e MME_CIDR_S1C="${DEFAULT_HOST_INTERFACE_IP}/32" \
        -e MME_CIDR_S11="${DEFAULT_HOST_INTERFACE_IP}/32" \
        -e MME_CIDR_S10="${DEFAULT_HOST_INTERFACE_IP}/32" \
        -e NW_IF_S1C="${DEFAULT_HOST_INTERFACE_NAME}" \
        -e NW_IF_S11="${DEFAULT_HOST_INTERFACE_NAME}" \
        -e NW_IF_S10="${DEFAULT_HOST_INTERFACE_NAME}" \
        -e SGW_CIDR_S11="127.0.0.1/32" \
        -e HSS_SERVICE="hss.openair4G.eur" \
        -e HSS_IP="$HSS_IP" \
        -e HSS_REALM="openair4G.eur" \
        -e HSS_HOSTNAME="hss" \
        -p 3870:3870 -p 5870:5870 -p 2123:2123 -p 36412:36412 \
        -v ${PWD}/manifests/oai-mme/certs:/opt/oai-mme/certs:Z \
        -h mme.openair4G.eur
fi

if [[ "${COMPONENTS}" =~ "RCC" ]]; then
    CPU_RT_PARAMS=""
    if [[ "$IS_RT_CAPABLE" == "false" ]]; then
        CPU_RT_PARAMS="--cpu-rt-period 1000 --cpu-rt-runtime 950" # allocate 95% of a 1ms period to rt tasks
    fi
    run_component oai-enb:1.1.0.${PLATFORM} oai-enb-rcc 127.0.0.1:2152 \
        ${CPU_RT_PARAMS} \
        --privileged \
        --cap-add=SYS_NICE \
        -e MODE="RCC" \
        -e ENB_ID="0xe00" \
        -e ENB_NAME="eNB_Eurecom_LTEBox" \
        -e TAC=1 \
        -e MCC=208 \
        -e MNC=92 \
        -e MME_IP4="127.0.0.2" \
        -e MME_IP6="::1" \
        -e S1_MME_IF="lo" \
        -e S1_MME_IP="127.0.0.1" \
        -e S1_U_IF="lo" \
        -e S1_U_IP="127.0.0.1" \
        -e X2C_IP="127.0.0.1" \
        -e RU_LOCAL_IF="${DEFAULT_HOST_INTERFACE_NAME}" \
        -e RU_LOCAL_IP="${DEFAULT_HOST_INTERFACE_IP}" \
        -e RU_REMOTE_IP="192.168.18.205" \
        -p 2152:2152 -p 36412:36412 -p 36422:36422 -p 50000:50000 -p 50001:50001
fi

if [[ "${COMPONENTS}" =~ "eNB+UE" ]]; then
    CPU_RT_PARAMS=""
    if [[ "$IS_RT_CAPABLE" == "false" ]]; then
        CPU_RT_PARAMS="--cpu-rt-period=1000 --cpu-rt-runtime=950" # allocate 95% of a 1ms period to rt tasks
    fi
    run_component oai-enb:1.1.0.${PLATFORM} oai-enb-rcc 127.0.0.1:2152 \
        ${CPU_RT_PARAMS} \
        --cap-add=SYS_NICE \
        -e MODE="RCC" \
        -e ENB_ID="0xe00" \
        -e ENB_NAME="eNB_Eurecom_LTEBox" \
        -e TAC=1 \
        -e MCC=208 \
        -e MNC=92 \
        -e MME_IP4="127.0.0.1" \
        -e MME_IP6="::1" \
        -e S1_MME_IF="lo" \
        -e S1_MME_IP="127.0.0.1" \
        -e S1_U_IF="lo" \
        -e S1_U_IP="127.0.0.1" \
        -e X2C_IP="127.0.0.1" \
        -e RU_LOCAL_IF="lo" \
        -e RU_LOCAL_IP="127.0.0.1" \
        -e RU_REMOTE_IP="127.0.0.1" \
        -p 2152:2152 -p 36412:36412 -p 36422:36422 -p 50000:50000 -p 50001:50001

    run_component oai-ue:1.1.0.${PLATFORM} oai-ue 127.0.0.1:2152 \
        ${CPU_RT_PARAMS} \
        --cap-add=SYS_NICE \
        -e RU_LOCAL_IF="lo" \
        -e RU_LOCAL_IP="127.0.0.1" \
        -e RU_REMOTE_IP="127.0.0.1" \
        -p 50000:50000 -p 50001:50001
fi

if [[ "${COMPONENTS}" =~ "GNB" ]]; then
    run_component oai-gnb:develop-nr.${PLATFORM} oai-gnb 127.0.0.1:2152 \
        --privileged --cap-add=SYS_RAWIO --cap-add=SYS_NICE --cap-add=IPC_LOCK \
        --device=/dev/cpu_dma_latency:/dev/cpu_dma_latency \
        -e GNB_ID="0xe00" \
        -e GNB_NAME="gNB-Eurecom-5GNRBox" \
        -e TAC=1 \
        -e MCC=208 \
        -e MNC=93 \
        -e MME_IP4="192.168.12.26" \
        -e MME_IP6="192:168:30::17" \
        -e S1_MME_IF="eth0" \
        -e S1_MME_IP="192.168.12.111/24" \
        -e S1_U_IF="lo" \
        -e S1_U_IP="127.0.0.1" \
        -e SDR_FIRST_IP="192.168.10.2" \
        -e SDR_SECOND_IP="192.168.40.2" \
        -e SDR_MGMT_IP="192.168.10.2" \
        -p 2152:2152
fi

if [[ "${COMPONENTS}" =~ "NR-UE" ]]; then
    run_component oai-nrue:develop-nr.${PLATFORM} oai-nrue 127.0.0.1:2152 \
        --privileged --cap-add=SYS_RAWIO --cap-add=SYS_NICE --cap-add=IPC_LOCK \
        --device=/dev/cpu_dma_latency:/dev/cpu_dma_latency \
        -e GNB_ID=42 \
        -e GNB_NAME="eNB" \
        -e MCC=42 \
        -e MNC=42 \
        -e MME_IP4="127.0.0.1" \
        -e MME_IP6="::1" \
        -e S1_MME_IF="lo" \
        -e S1_MME_IP="127.0.0.1" \
        -e S1_U_IF="lo" \
        -e S1_U_IP="127.0.0.1" \
        -e SDR_FIRST_IP="127.0.0.1" \
        -e SDR_SECOND_IP="127.0.0.2" \
        -e SDR_MGMT_IP="127.0.0.3" \
        -p 2152:2152
fi

info "All services started."
